package com.wang.tool.monitor;


import com.alibaba.fastjson.JSON;
import com.wang.tool.common.ErrorInfo;
import com.wang.tool.common.Pair;
import com.wang.tool.monitor.core.LinkInfo;
import com.wang.tool.monitor.core.MonitorApi;
import com.wang.tool.monitor.core.MonitorInfo;
import com.wang.tool.monitor.fastover.FastoverFactory;
import com.wang.tool.monitor.fastover.FastoverInfo;
import com.wang.tool.monitor.template.Invoker;
import com.wang.tool.monitor.template.MonitorInvoker;
import com.wang.tool.monitor.translator.GlobalErrorTranslator;
import com.wang.tool.monitor.weaver.MonitorWeaver;

import java.util.function.Supplier;

/**
 * @Description 执行模板
 * 抓取异常执行
 * 带摘要执行
 * 配置变更
 * @Author shaopeng
 * @Date 2021/11/30
 * @Version 1.0
 * @see MonitorConfigCenter
 */
public class InvokeTemplate {

    public static final String TAG_ERROR_CATCH = "ErrorCatch";
    public static final String TAG_ERROR_ON_ENTRANCE = "ErrorOnEntrance";
    public static final String TAG_REPLACE_DATA = "ReplaceData";
    public static final String TAG_CHANGE_DATA = "ChangeData";

    /**
     * 抓取异常执行 不带监控
     * 场景:将异常包装成错误结果返回
     *
     * @param invoker 执行器
     * @param <R>
     * @return
     * @see Invoker
     */
    public static <R> R invoke(Invoker<R> invoker) {
        return invoke(null, invoker);
    }

    /**
     * 抓取异常执行 不带监控
     * 场景:将异常包装成错误结果返回
     *
     * @param action  动作码,异常被抓取后日志上会带着
     * @param invoker 执行器
     * @param <R>
     * @return
     * @see Invoker
     */
    public static <R> R invoke(String action, Invoker<R> invoker) {
        try {
            return invoker.internalCall();
        } catch (Throwable e) {
            //格式统一
            action = action == null ? ":" : (":" + action);
            ErrorInfo error = GlobalErrorTranslator.translate(e);
            R result = invoker.handleError(e, error);
            LinkLogUtil.newTagLog(TAG_ERROR_CATCH)
                    .v1(error.getErrorCode())
                    .v2(action)
                    .log();
            LinkLogUtil.error(TAG_ERROR_CATCH, e, "[{0}][{1}][{2}]", action, error.getErrorCode(), error.getErrorMsg());
            return result;
        }
    }



    /*-------------------------分割线-------------------------*/

    /**
     * 摘要执行 带监控
     * 等同于 @Monitor+异常抓取
     *
     * @param <R>
     * @param biz     业务类
     * @param action  双码-动作 建议非空
     * @param invoker 执行器
     * @return
     * @see MonitorInvoker
     */
    public static <R> R monitor(Class<?> biz, String action, MonitorInvoker<R> invoker) {
        return monitor(null, biz == null ? null : biz.getSimpleName(), action, invoker);
    }

    /**
     * 摘要执行 带监控
     * 等同于 @Monitor+异常抓取
     *
     * @param <R>
     * @param biz     双码-业务 建议非空
     * @param action  双码-动作 建议非空
     * @param invoker 执行器
     * @return
     * @see MonitorInvoker
     */
    public static <R> R monitor(String biz, String action, MonitorInvoker<R> invoker) {
        return monitor(null, biz, action, invoker);
    }

    /**
     * 摘要执行 带监控
     * 等同于 @Monitor+异常抓取
     *
     * @param <R>
     * @param group   分组
     * @param biz     双码-业务 建议非空
     * @param action  双码-动作 建议非空
     * @param invoker 执行器
     * @return
     * @see MonitorInvoker
     */
    public static <R> R monitor(String group, String biz, String action, MonitorInvoker<R> invoker) {
        if (invoker == null) {
            return null;
        }
        long begin = System.currentTimeMillis();

        group = group != null ? group : "";
        biz = biz != null ? biz : "";
        action = action != null ? action : "";

        ErrorInfo error = null;
        R result = null;
        MonitorInfo<R> monitorInfo = null;
        try {
            Object[] params;
            //步入监控
            try {
                params = invoker.getParams();
                params = params == null ? MonitorInvoker.EMPTY_PARAM : params;
            } catch (Throwable ignore) {
                params = MonitorInvoker.EMPTY_PARAM;
            }

            monitorInfo = MonitorApi.stepInMonitor(group, biz, action, invoker, params, begin);

            //fastover
            FastoverInfo fastover = monitorInfo == null ? null : monitorInfo.getFastoverInfo();
            if (fastover != null) {
                if (fastover.getFail() != null) {
                    monitorInfo.addTag(MonitorInfo.TAG_FASTOVER, "fastFail", null);
                    throw fastover.getFail();
                }

                //取方法上泛型的真实值,理论上应该不会报错
                Class<?> returnType = invoker.getClass().getMethod("internalCall").getReturnType();
                Pair<Boolean, ?> fitResult = fastover.resultIsFit(returnType);
                if (fitResult.getLeft()) {
                    monitorInfo.addTag(MonitorInfo.TAG_FASTOVER, "fastReturn", null);
                    return result = (R) fitResult.getRight();
                }
                //不正常
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(biz + ":" + action)
                        .v2("fastReturnNotFit")
                        .addAttachment("returnType", returnType.getName())
                        .addAttachment("realType", fastover.getResult() == null ? null : fastover.getResult().getClass().getName())
                        .pointLink(monitorInfo.getLink())
                        .log();
            }

            return result = invoker.internalCall();
        } catch (Throwable e) {
            //抓取了异常
            if (monitorInfo != null) {
                monitorInfo.markImplicitError();
            }
            //翻译
            error = GlobalErrorTranslator.translate(e);
            try {
                //处理异常
                result = invoker.handleError(e, error);
                //既然异常被吞掉了,那么这里应该置回空,当做成功处理,同时记录异常
                LinkLogUtil.newTagLog(TAG_ERROR_CATCH)
                        .v1(biz + ":" + action)
                        .v2(error.getErrorCode())
                        .pointLink(monitorInfo == null ? null : monitorInfo.getLink())
                        .log();
                LinkLogUtil.error(TAG_ERROR_CATCH, e, "[{0}][{1}][{2}]", biz + ":" + action, error.getErrorCode(), error.getErrorMsg());
                error = null;
            } catch (Throwable e2) {
                error = e2 == e ? error : GlobalErrorTranslator.translate(e2);
                //异常正常抛出,这时如果是根节点,那么我们也需要打印异常堆栈
                if (monitorInfo != null && monitorInfo.isRoot()) {
                    LinkLogUtil.newTagLog(TAG_ERROR_ON_ENTRANCE)
                            .v1(biz + ":" + action)
                            .v2(error.getErrorCode())
                            .pointLink(monitorInfo.getLink())
                            .log();
                    LinkLogUtil.error(TAG_ERROR_ON_ENTRANCE, e2, "[{0}][{1}][{2}]", biz + ":" + action, error.getErrorCode(), error.getErrorMsg());
                }
                throw e2;
            }
            return result;
        } finally {
            MonitorApi.stepOutMonitor(monitorInfo, result, error, System.currentTimeMillis());
        }
    }


    /**
     * 快速结束取值(从MonitorOpsCenter取配置)
     *
     * @param group
     * @param biz    非空
     * @param action 非空
     * @param time   时间戳,根据时间戳和配置的时间区间做筛选
     * @param params 参数,根据白名单配置进行筛选
     * @return 有配置返回则根据配置做处理, 无配置返回则说明需要正常执行业务
     */
    public static FastoverInfo fastover(String group, String biz, String action, long time, Object[] params) {
        return MonitorApi.fastover(group, biz, action, time, params);
    }


    /**
     * 快速结束取值(从独立工厂取配置)
     *
     * @param key    名称
     * @param time   时间戳,根据时间戳和配置的时间区间做筛选
     * @param params 参数,根据白名单配置进行筛选
     * @return 有配置返回则根据配置做处理, 无配置返回则说明需要正常执行业务
     */
    public static FastoverInfo fastoverByFactory(String key, long time, Object[] params) {
        return FastoverFactory.fastover(key, time, params);
    }


    /**
     * 记录链路
     * 适用于在执行过后节点补全,例如某异步调用无法直接包裹执行,在执行过后进行补全
     *
     * @param parent    指定父链路 不会使用当前线程变量
     * @param group     分组
     * @param biz       业务码
     * @param action    动作码
     * @param beginTime 开始时间
     * @param params    参数
     * @param endTime   结束时间
     * @param result    结果
     * @param e         异常
     * @param weaver    编织器
     * @param <R>
     */
    public static <R> MonitorInfo<R> recordLink(LinkInfo parent, String group, String biz, String action, long beginTime, Object[] params, long endTime, R result, Exception e, MonitorWeaver<R> weaver) {
        return MonitorApi.recordLink(parent, group, biz, action, beginTime, params, endTime, result, e == null ? null : GlobalErrorTranslator.translate(e), weaver);
    }

    /*-------------------------分割线-------------------------*/


    /**
     *
     * @param name
     * @param old
     * @param newData
     * @return
     * @param <T>
     */
    public static <T> T replace(String name, T old, T newData) {
        LinkLogUtil.info("CONFIG_REPLACE, name:{0}, old:{1}, new:{2}", name,  old, newData);
        return newData;
    }

    /**
     * Record service switching option
     * @param name
     * @param point
     */
    public static void recordPoint(String name, boolean point) {
        LinkLogUtil.newTagLog("CONFIG_RECORD").v1(name).v2(String.valueOf(point)).log();
    }

    /**
     * 尝试替换
     * 配置解析,出现异常返回老配置
     *
     * @param name   配置名
     * @param old    老配置
     * @param parser 解析器
     * @param <T>
     * @return
     */
    public static <T> T tryReplace(String name, T old, Supplier<T> parser) {
        ErrorInfo error = null;
        T newData = null;
        try {
            return newData = parser.get();
        } catch (Throwable e) {
            error = GlobalErrorTranslator.translate(e);
            LinkLogUtil.error(TAG_REPLACE_DATA, e, "[{0}][{1}][{2}]", name, error.getErrorCode(), error.getErrorMsg());
            return old;
        } finally {
            LinkLogUtil.newTagLog(TAG_REPLACE_DATA)
                    .v1(name)
                    .v2(error == null ? "noError" : "hasError")
                    .addAttachment(MonitorInfo.ERROR, error)
                    .addAttachment("old", JSON.toJSONString(old))
                    .addAttachment("new", JSON.toJSONString(newData))
                    .log();
        }
    }


    /**
     * 尝试变更
     *
     * @param name    配置名
     * @param changer 变更器 返回变更结果 null代表变更成功
     * @return
     */
    public static void tryChange(String name, Supplier<String> changer) {
        ErrorInfo error = null;
        String status = null;
        try {
            status = changer.get();
            status = status == null ? "success" : status;
        } catch (Throwable e) {
            error = GlobalErrorTranslator.translate(e);
            status = error.getErrorCode();
            LinkLogUtil.error(TAG_CHANGE_DATA, e, "[{0}][{1}][{2}]", name, error.getErrorCode(), error.getErrorMsg());
        } finally {
            LinkLogUtil.newTagLog(TAG_CHANGE_DATA)
                    .v1(name)
                    .v2(status)
                    .addAttachment(MonitorInfo.ERROR, error)
                    .log();
        }
    }

}
